﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace katOS
{
    /// <summary>
    /// Class that interprets a line of code of a user program from katOS
    /// </summary>
    class Interpreter
    {
        Parser a_parser=new Parser();
        UserPrograms a_program = new UserPrograms();

        /// <summary>
        /// Method that excecutes one instruction from determinated process
        /// </summary>
        /// <param name="new_process">New Process to be excecuted</param>
        /// <param name="inst">Instruction to be excecuted from the new_process</param>
        /// <param name="tempip">IP that corresponds to the instruction to be excecuted</param>
        public void Excecute_One(Process new_process, string inst, int tempip)
        {
            String[] part_instruction;
            part_instruction = inst.Split(' ');
            switch (part_instruction[0])
            {
                case "set":
                    if (part_instruction.Length == 3)
                    {
                        a_program.Set(new_process, part_instruction[1], part_instruction[2]);
                    }
                    else
                    {
                        Console.WriteLine("Wrong parameters for a Set instruction");
                    }
                    break;

                case "add":
                    if (part_instruction.Length == 4)
                    {
                        a_program.Add(new_process, part_instruction[1], part_instruction[2], part_instruction[3]);
                    }
                    else
                    {
                        Console.WriteLine("Wrong parameters for an Add instruction");
                    }
                    break;

                case "sub":
                    if (part_instruction.Length == 4)
                    {
                        a_program.Sub(new_process, part_instruction[1], part_instruction[2], part_instruction[3]);
                    }
                    else
                    {
                        Console.WriteLine("Wrong parameters for a Sub instruction");
                    }
                    break;

                case "mul":
                    if (part_instruction.Length == 4)
                    {
                        a_program.Mul(new_process, part_instruction[1], part_instruction[2], part_instruction[3]);
                    }
                    else
                    {
                        Console.WriteLine("Wrong parameters for a Mul instruction");
                    }
                    break;

                case "div":
                    if (part_instruction.Length == 5)
                    {
                        a_program.Div(new_process, part_instruction[1], part_instruction[2], part_instruction[3], part_instruction[4]);
                    }
                    else
                    {
                        Console.WriteLine("Wrong parameters for a Div instruction");
                    }
                    break;

                case "random":
                    if (part_instruction.Length == 4)
                    {
                        a_program.Random(new_process, part_instruction[1], part_instruction[2], part_instruction[3]);
                    }
                    else
                    {
                        Console.WriteLine("Wrong parameters for Random instruction");
                    }
                    break;

                case "jmp":
                    if (part_instruction.Length == 3)
                    {
                        a_program.Jmp(new_process, part_instruction[1], part_instruction[2], new_process.getSymbolsTable());
                    }
                    else
                    {
                        Console.WriteLine("Wrong parameters for a Jmp instruction");
                    }
                    break;

                case "echo":
                    if (part_instruction.Length == 2)
                    {
                        string a_string = part_instruction[1];
                        a_program.Echo(new_process, a_string);
                    }
                    else if (part_instruction.Length >= 2)
                    {
                        string some = "";
                        for (int i = 1; i < part_instruction.Length; i++)
                        {
                            some = some + part_instruction[i];
                            some = some + " ";
                        }
                        some = some.TrimEnd(' ');
                        string a_string = some;
                        a_program.Echo(new_process, a_string);  
                    }
                    else
                    {
                        Console.WriteLine("Wrong parameters for an Echo instruction");
                    }
                    break;

                case "openfile":
                    a_program.OpenFile(new_process,part_instruction[1],part_instruction[2]);
                    break;

                case "closefile":
                    a_program.CloseFile(new_process, part_instruction[1]);
                    break;

                case "readline":
                    a_program.ReadLine(new_process, part_instruction[1], part_instruction[2]);
                    break;

                case "sleep":
                    a_program.Sleep(new_process);
                    break;

                case "end":
                    a_program.End(new_process, 0+"",true);
                    break;

                case "fork":
                    a_program.Fork(new_process, part_instruction[1], part_instruction[2]);
                    break;

                case "getstate":
                    a_program.getState(new_process, part_instruction[1]);
                    break;

                case "wait":
                    a_program.Wait(new_process,part_instruction[1]);
                    break;
                
                case "signal":
                    a_program.Signal(new_process, part_instruction[1]);
                    break;

                default:
                    break;
            }
        }
        
        /// <summary>
        /// Method that checks if the end of a file has been reached
        /// </summary>
        /// <param name="new_process">Process to check</param>
        /// <returns>Returns true if the process has terminated</returns>
        public bool CheckEnd(Process new_process)
        {

            if (!new_process.hasReachedEnd())
            {
                new_process.setState(3);
                try
                {
                    Excecute_One(new_process, new_process.getInstructions().ElementAt(new_process.getIP()), new_process.getIP());
                }
                catch (Exception) { }
                new_process.setIP(new_process.getIP() + 1);
                return false;
            }
            else
            {
                //add to terminated process list
                a_program.End(new_process, 0 + "", true);
                Scheduler.terminated_processes.AddLast(new_process);
                Console.WriteLine();
                return true;
            }
        }
    }
}
